using System;
using System.Collections.Generic;
using PaintAWorld.ChunkUpdates;
using ZFramework;
using ZFramework.Voxel;

namespace PaintAWorld
{
    public class WorldLogic : IVoxelMap<Rgba32>
    {
        private VoxelModel _data;
        private Player _player;

        private IMessenger _messenger;    

        public WorldLogic(IMessenger messenger)
        {
            _messenger = messenger;

            _player = new(this, Vec3.Zero);

            var rand = new Random();
            _data = new VoxelModel(16, 16, 16);
            for (var x = 0; x < 16; x++)
            {
                for (var z = 0; z < 16; z++)
                {
                    for (var y = 0; y < 16; y++)
                    {
                        _data.SetColor(x, y, z, Rgba32.Green);
                    }
                }
            }

            _messenger.Broadcast(new WorldChunkUpdateEvent(Vec3I.Zero, new TerrainUpdate(_data)));
        }

        public WorldLogic(IMessenger messenger, VoxelModel model)
        {
            _messenger = messenger;

            _player = new(this, Vec3.Zero);

            _data = model;

            _messenger.Broadcast(new WorldChunkUpdateEvent(Vec3I.Zero, new TerrainUpdate(_data)));
        }

        public void DispatchInput(PlayerInput input)
        {
            _player.HandleInput(input.content);
        }

        public bool HasVoxel(int x, int y, int z) => _data.HasVoxel(x, y, z);

        public Rgba32 VoxelAt(int x, int y, int z) => _data.ColorAt(x, y, z);

        private void SetVoxelSlient(Vec3I voxelPos, Rgba32 color)
        {
            if (_data.BoundingBox.Includes(voxelPos.x, voxelPos.y, voxelPos.z))
            {
                _data.SetColor(voxelPos.x, voxelPos.y, voxelPos.z, color);
            }
        }

        public void SetVoxel(Vec3I voxelPos, Rgba32 color)
        {
            SetVoxelSlient(voxelPos, color);
            _messenger.Broadcast(new WorldChunkUpdateEvent(Vec3I.Zero, new TerrainUpdate(_data)));
        }

        public void SetVoxelBatched(IEnumerable<Vec3I> voxelPositions, Rgba32 color)
        {
            foreach (var pos in voxelPositions)
            {
                SetVoxelSlient(pos, color);
            }
            _messenger.Broadcast(new WorldChunkUpdateEvent(Vec3I.Zero, new TerrainUpdate(_data)));
        }

        private void RemoveVoxelSlient(Vec3I voxelPos)
        {
            if (_data.BoundingBox.Includes(voxelPos.x, voxelPos.y, voxelPos.z))
            {
                _data.RemoveAt(voxelPos.x, voxelPos.y, voxelPos.z);
            }
        }

        public void RemoveVoxel(Vec3I voxelPos)
        {
            RemoveVoxelSlient(voxelPos);
            _messenger.Broadcast(new WorldChunkUpdateEvent(Vec3I.Zero, new TerrainUpdate(_data)));
        }

        public void RemoveVoxelBatched(IEnumerable<Vec3I> voxelPositions)
        {
            foreach (var pos in voxelPositions)
            {
                RemoveVoxelSlient(pos);
            }
            _messenger.Broadcast(new WorldChunkUpdateEvent(Vec3I.Zero, new TerrainUpdate(_data)));
        }
    }
}